package com.skycan.plc.adapter.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.skycan.plc.adapter.config.AppConfig;
import com.skycan.plc.adapter.config.MqttConfig;
import com.skycan.plc.adapter.model.DataPoint;
import com.skycan.plc.adapter.model.DeviceInfo;
import com.skycan.plc.adapter.mqtt.MqttClient;
import com.skycan.plc.adapter.protocol.ProtocolAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 数据采集服务
 */
public class DataCollectionService {
    private static final Logger logger = LoggerFactory.getLogger(DataCollectionService.class);

    private final DeviceService deviceService;
    private final MqttClient mqttClient;
    private final ObjectMapper objectMapper;
    private final ScheduledExecutorService scheduler;

    // 采集配置，格式为Map<设备ID, Map<地址, 数据类型>>
    private final Map<String, Map<String, DataPoint.DataType>> collectionConfig = new HashMap<>();

    // 采集间隔(毫秒)
    private final long pollingInterval;

    // 数据上报主题
    private final String dataTopic;

    public DataCollectionService(DeviceService deviceService) {
        this.deviceService = deviceService;
        this.mqttClient = MqttClient.getInstance();
        this.objectMapper = new ObjectMapper();
        this.scheduler = Executors.newScheduledThreadPool(5);
        this.pollingInterval = AppConfig.getInstance().getDevicePollingInterval();
        this.dataTopic = MqttConfig.getInstance().getDataTopic();
    }

    /**
     * 启动数据采集服务
     */
    public void start() {
        logger.info("数据采集服务启动，轮询间隔: {}毫秒", pollingInterval);

        // 启动数据采集任务
        scheduler.scheduleAtFixedRate(this::collectData, 0, pollingInterval, TimeUnit.MILLISECONDS);
    }

    /**
     * 停止数据采集服务
     */
    public void stop() {
        logger.info("数据采集服务停止");

        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown();
            try {
                if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow();
                }
            } catch (InterruptedException e) {
                scheduler.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }

    /**
     * 添加采集配置
     *
     * @param deviceId 设备ID
     * @param address  地址
     * @param type     数据类型
     */
    public void addCollectionPoint(String deviceId, String address, DataPoint.DataType type) {
        collectionConfig.computeIfAbsent(deviceId, k -> new HashMap<>())
                .put(address, type);

        logger.info("添加采集点: 设备={}, 地址={}, 类型={}", deviceId, address, type);
    }

    /**
     * 移除采集配置
     *
     * @param deviceId 设备ID
     * @param address  地址
     */
    public void removeCollectionPoint(String deviceId, String address) {
        Map<String, DataPoint.DataType> deviceConfig = collectionConfig.get(deviceId);
        if (deviceConfig != null) {
            deviceConfig.remove(address);
            logger.info("移除采集点: 设备={}, 地址={}", deviceId, address);
        }
    }

    /**
     * 批量添加采集配置
     *
     * @param deviceId       设备ID
     * @param addressTypeMap 地址类型映射
     */
    public void addCollectionPoints(String deviceId, Map<String, DataPoint.DataType> addressTypeMap) {
        collectionConfig.computeIfAbsent(deviceId, k -> new HashMap<>())
                .putAll(addressTypeMap);

        logger.info("批量添加采集点: 设备={}, 点数={}", deviceId, addressTypeMap.size());
    }

    /**
     * 数据采集任务
     */
    private void collectData() {
        // 获取当前所有注册的设备
        Map<String, DeviceInfo> deviceInfoMap = deviceService.getAllDeviceInfo();

        for (Map.Entry<String, DeviceInfo> entry : deviceInfoMap.entrySet()) {
            String deviceId = entry.getKey();
            DeviceInfo deviceInfo = entry.getValue();

            // 如果设备离线或异常，跳过采集
            if (deviceInfo.getStatus() != DeviceInfo.DeviceStatus.ONLINE) {
                continue;
            }

            // 获取设备的采集配置
            Map<String, DataPoint.DataType> deviceConfig = collectionConfig.get(deviceId);
            if (deviceConfig == null || deviceConfig.isEmpty()) {
                continue;
            }

            // 获取设备对应的协议适配器
            ProtocolAdapter adapter = deviceService.getProtocolAdapter(deviceInfo.getDeviceType());
            if (adapter == null) {
                logger.error("未找到设备协议适配器: {}, 类型: {}", deviceId, deviceInfo.getDeviceType());
                continue;
            }

            try {
                // 准备采集的地址和类型列表
                List<String> addresses = new ArrayList<>();
                List<DataPoint.DataType> types = new ArrayList<>();

                for (Map.Entry<String, DataPoint.DataType> configEntry : deviceConfig.entrySet()) {
                    addresses.add(configEntry.getKey());
                    types.add(configEntry.getValue());
                }

                // 读取数据点
                List<DataPoint> dataPoints = adapter.readDataPoints(deviceInfo, addresses, types);

                // 上报数据
                for (DataPoint dataPoint : dataPoints) {
                    reportDataPoint(dataPoint);
                }
            } catch (Exception e) {
                logger.error("设备数据采集异常: {}, 错误: {}", deviceId, e.getMessage());

                // 更新设备状态
                deviceService.updateDeviceStatus(deviceId, DeviceInfo.DeviceStatus.ERROR);
            }
        }
    }

    /**
     * 上报数据点
     *
     * @param dataPoint 数据点
     */
    private void reportDataPoint(DataPoint dataPoint) {
        try {
            // 将数据点转换为JSON
            String json = objectMapper.writeValueAsString(dataPoint);

            // 发布到MQTT主题
            boolean success = mqttClient.publish(dataTopic, json);

            if (success) {
                logger.debug("数据点上报成功: {}, 值: {}", dataPoint.getAddress(), dataPoint.getValue());
            } else {
                logger.error("数据点上报失败: {}", dataPoint.getAddress());
            }
        } catch (JsonProcessingException e) {
            logger.error("数据点序列化异常: {}", e.getMessage());
        }
    }
}